# -*- coding: utf-8 -*-

import os
import re
import requests
import hashlib
import sqlite3

from pyfzf.pyfzf import FzfPrompt
from os.path import exists
from bs4 import BeautifulSoup as bs

print("Загрузка....")

fzf = FzfPrompt()


def get_hash_md5(filename):
    """Функция дла проверки хэш суммы базы данных"""
    # Открываем файл на чтение в формате байтов
    with open(filename, "rb") as f:
        m = hashlib.md5()
        while True:
            # Тут можно изменить значение количества выгружаемых байт
            data = f.read(8192)
            if not data:
                break
            # Обновляем сумму
            m.update(data)
        return m.hexdigest()


def choice_video():
    """ Функция отвечает за вывод всех видео """
    # Подключаемся к БД
    db = sqlite3.connect("channels_base.db")
    # Устанавливаем вывод в формате id: name
    db.row_factory = lambda cursor, row: row[0]
    c = db.cursor()

    # Выгружаем id и name
    ids = c.execute("SELECT NAME FROM channels").fetchall()

    # Выводим список каналов
    print(i + "\n" for i in ids)

    # Выбираем нужный канал
    target_channel = fzf.prompt(ids)

    # Выводим выбранный канал в консоль
    print(target_channel[0])
    # Получаем id выбранного канала
    channel_number = ids.index(target_channel[0])
    # Выгружаем url канала
    url = subscribes[channel_number].replace("\n", "")

    source = requests.get(url)
    source.encoding = "utf-8"

    # Парсим видео с канала
    soup = bs(source.text, "html.parser")
    channel = soup.select("span")[1].text

    # Объявляем список видео
    names_list = []
    z = 0

    # Парсим и ищем сами видео
    for title in soup.select(".pure-u-1.pure-u-md-1-4"):
        link = title.find("a")

        # Проверяем на наличие в элементе ссылки
        if link is not None:
            # Удаляем все переносы строки в начале и в конце, а так же
            # приводим к адекватному формату длительность видео
            video_info = link.text.strip("\n").replace("\n\n", " | ")
            # Проверяем, не является ли элемент названием канала
            if re.search(channel, video_info):
                continue
            else:
                z += 1
                if z >= 2:
                    names_list.append(video_info)

    # Выбираем видео из списка найденных
    target = fzf.prompt(names_list)
    # Выводим выбранное видео в консоль
    print(target[0])
    # Получаем индекс выбранного видео
    number = names_list.index(target[0])
    # Объявляем список ссылок на видео
    watches_list = []

    # Ищем все ссылки на сайте
    for a in soup.find_all("a", href=True):
        link = a["href"]
        # Отсеиваем среди них те, что принадлежат ссылкам на видео и заносим
        # их в список
        if re.search(r"com/watch\b", link):
            watches_list.append(link)
    # Получаем ссылку выбранного видео
    end_target = watches_list[number+1]

    # Запускаем наш плеер
    command = "mpv " + end_target
    os.system(command)


def get_saved_channels():
    """Возвращает список сохраненных каналов"""
    sql = "SELECT URL FROM channels"
    return [url[0] for url in cursor.execute(sql).fetchall()]


def remove_channels(saved_channels):
    """Удаляет все каналы, что отсутствуют в подписках"""
    channels_for_delete = []

    for saved_channel in saved_channels:
        if saved_channel not in channels:
            channels_for_delete.append(saved_channel)

    sql = "DELETE FROM channels WHERE URL IN ({})"
    in_p = ", ".join(list(map(lambda x: "?", channels_for_delete)))
    sql = sql.format(in_p)
    cursor.execute(sql, channels_for_delete)


while True:
    # Основной цикл приложения
    channel_list = []

    # Флаг необходимости динамического обновление подписок в БД
    need_update_subscribes = False

    # Выгрузка всех каналов, записанных в subscribe.txt
    with open("subscribe.txt", "r") as subs:
        subscribes = subs.readlines()

    # Проверяем хэш записанных в файл подписок
    with open("subscribes_hash.txt", "r") as subs_hash:
        # В случае наличия отличий ставим флаг о необходимости обновления
        if subs_hash.read() != get_hash_md5("subscribe.txt"):
            need_update_subscribes = True
            # Записываем новое значение хэш функции.
            open("subscribes_hash.txt", "w").write(
                get_hash_md5("subscribe.txt")
            )

    # Проверяем на наличие базы и на отсутствие флага о необходимости
    # обновления базы
    if exists("channels_base.db") and not need_update_subscribes:
        choice_video()

    else:
        print("Генерируем базу каналов, это может занять некоторое время...")

        # Создаем и сразу подключаемся к новой базе данных
        db = sqlite3.connect("channels_base.db")
        cursor = db.cursor()
        # Если таблицы нет, то создаем её. Она будет хранить название канала
        # и ссылку на него
        cursor.execute(
            """
            CREATE TABLE IF NOT EXISTS channels (
                ID INTEGER PRIMARY KEY,
                NAME TEXT,
                URL TEXT
            )
            """
        )
        # Заносим и фиксируем изменения.
        db.commit()

        # Хранилище пар URL:NAME
        channels = {}

        # Проходимся по всем каналам и выгружаем их названия
        for channel_url in subscribes:
            channel_url = channel_url.replace("\n", "")

            source = requests.get(channel_url)
            source.encoding = "utf-8"

            soup = bs(source.text, "html.parser")

            channel_name = soup.select("span")[1].text
            channels.update({channel_url: channel_name})

        # Получаем список сохраненных в БД каналов
        saved_channels = get_saved_channels()

        # Объявляем список каналов, которые необходимо добавить в БД
        channels_to_commit = []

        for channel in channels:
            # Проверяем канал на наличие в БД. Если его там не оказалось,
            # то заносим.
            if channel not in saved_channels:
                channels_to_commit.append([channels[channel], channel])
                print(channels[channel] + "  Добавлен в базу!  ")
                channel_list.append(channels[channel])

        sql = "INSERT INTO channels (NAME, URL) VALUES (?, ?)"

        # Делаем множественную запись короткой и быстрой за счет
        # executemany, вместо execute
        cursor.executemany(sql, channels_to_commit)

        # Удаляем из БД каналы, которых нет в файле подписок
        remove_channels(saved_channels)
        # Заносим изменения
        db.commit()
        # Запускаем основную функцию для выбора канала и видео
        choice_video()
